package pri.zxx.webdemo.config.timemonitor;

import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;


/**
 * <p>description</p>
 *
 * @author <a href="mailto:zxxcof@qq.com">zxx</a>
 * @since 2020/1/19 11:42
 */
@Aspect
@Component
public class ExecutionTimeAspect {
	@Value("${monitor.execution.time.threshold:1000}")
	private Integer threshold;
	private static final Logger logger = LoggerFactory.getLogger("ExecutionTimeMonitor");
	public static final ThreadLocal<MethodTrack> root = new ThreadLocal<>();
	public static final ThreadLocal<MethodTrack> current = new ThreadLocal<>();

	@Before("execution(* com.crb.management.controller..*.*(..))||execution(* com.crb.management.service..*.*(..))||execution(* com.crb.management.dao..*.*(..))")
	public void timeRecord(JoinPoint point) {
		if (notWebRequest()) {
			return;
		}
		String methodName = point.getSignature().getDeclaringTypeName() + '.' + point.getSignature().getName();
		if (root.get() == null) {
			MethodTrack rootMethod = new MethodTrack();
			rootMethod.setStart(System.currentTimeMillis());
			rootMethod.setMethodName(methodName);
			root.set(rootMethod);
			current.set(rootMethod);
		} else {
//            MethodTrack track = new MethodTrack(methodName, System.currentTimeMillis(), current.get());
			MethodTrack track = current.get().findChild(methodName);
			if (track == null) {
				track = new MethodTrack(methodName, System.currentTimeMillis(), current.get());
				current.get().getChildren().add(track);
			} else {
				track.setStart(System.currentTimeMillis());
			}

			current.set(track);
		}
	}

	@After("execution(* com.crb.management.controller..*.*(..))||execution(* com.crb.management.service..*.*(..))||execution(* com.crb.management.dao..*.*(..))")
	public void afterTimeRecord() {
		if (notWebRequest()) {
			return;
		}
		MethodTrack now = current.get();
		now.setEnd(System.currentTimeMillis());
		now.setCost();
		now.setTotal(now.getTotal() + now.getCost());
		now.setCount(now.getCount() + 1);
		now.setAvg(now.getTotal() / now.getCount());
		now.setMax(Math.max(now.getCost(), now.getMax()));
		now.setMin(Math.min(now.getCost(), now.getMin()));

		if (now == root.get()) {
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
			String uriQuery = request.getRequestURI();
			String query = request.getQueryString();
			if (StringUtils.isNotBlank(query)) {
				uriQuery = uriQuery + "?" + query;
			}
			if (root.get().getCost() > threshold) {
				logger.error("execution time monitor,slow request found,uri and query: {}\n{}", uriQuery, root.get().toString());
			}
			current.remove();
			root.remove();
		} else {
			current.set(current.get().getParent());
		}
	}

	private boolean notWebRequest() {
		return RequestContextHolder.getRequestAttributes() == null;
	}
}
